package com.bstek.uflo.designer.deserializer.impl;

import java.text.ParseException;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang.time.DateUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.bstek.uflo.designer.deserializer.ProcessDeserializer;
import com.bstek.uflo.designer.deserializer.ShapeDeserializer;
import com.bstek.uflo.designer.model.AssignmentType;
import com.bstek.uflo.designer.model.Shape;
import com.bstek.uflo.designer.model.SwimlaneAssignee;
import com.bstek.uflo.designer.model.node.Node;
import com.bstek.uflo.designer.model.process.Process;
import com.bstek.uflo.designer.model.process.Swimlane;

/**
 * @author matt.yao@bstek.com
 * @since 1.0
 */
@Service(ProcessDeserializer.BEAN_ID)
public class DefaultProcessDeserializer implements ProcessDeserializer, ApplicationContextAware {

	private Collection<ShapeDeserializer> shapeDeserializerColl;

	public Process execute(Document document) throws Exception {
		Assert.notNull(document);
		Element rootElement = document.getRootElement();
		Process process = new Process();
		this.deserializerCommonAttribute(process, rootElement);
		this.deserializerSwimlane(process, rootElement);
		this.deserializerNode(process, rootElement);
		return process;
	}

	private void deserializerCommonAttribute(Process process, Element rootElement) {
		String name = rootElement.attributeValue("name");
		String key = rootElement.attributeValue("key");
		String startProcessUrl = rootElement.attributeValue("start-process-url");
		String eventHandlerBean = rootElement.attributeValue("event-handler-bean");
		String categoryId = rootElement.attributeValue("category-id");
		String effectDate = rootElement.attributeValue("effect-date");
		process.setName(name);
		process.setKey(key);
		process.setStartProcessUrl(startProcessUrl);
		process.setEventHandlerBean(eventHandlerBean);
		process.setCategoryId(categoryId);
		if (StringUtils.hasText(effectDate)) {
			try {
				process.setEffectDate(DateUtils.parseDate(effectDate, new String[] { "yyyy-MM-dd HH:mm:ss" }));
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}
		Element descriptionElement = rootElement.element("description");
		if (descriptionElement != null) {
			String desc = descriptionElement.getText();
			process.setDescription(desc);
		}
	}

	private void deserializerSwimlane(Process process, Element rootElement) {
		List<?> swimlaneElements = rootElement.elements("swimlane");
		for (Object swimlaneObj : swimlaneElements) {
			Element swimlaneElement = (Element) swimlaneObj;
			String swimlaneName = swimlaneElement.attributeValue("name");
			String assignmentType = swimlaneElement.attributeValue("assignment-type");
			Swimlane swimlane = new Swimlane();
			swimlane.setName(swimlaneName);
			swimlane.setAssignmentType(assignmentType);
			if (assignmentType.equals(AssignmentType.Expression.name())) {
				String expression = swimlaneElement.attributeValue("expression");
				swimlane.setExpression(expression);
			} else if (assignmentType.equals(AssignmentType.Handler.name())) {
				String assignmentHandlerBean = swimlaneElement.attributeValue("assignment-handler-bean");
				swimlane.setAssignmentHandlerBean(assignmentHandlerBean);
			} else if (assignmentType.equals(AssignmentType.Assignee.name())) {
				List<?> assigneeElements = swimlaneElement.elements("assignee");
				for (Object assigneeObj : assigneeElements) {
					Element assigneeElement = (Element) assigneeObj;
					SwimlaneAssignee swimlaneAssignee = new SwimlaneAssignee();
					swimlaneAssignee.setId(assigneeElement.attributeValue("id"));
					swimlaneAssignee.setName(assigneeElement.attributeValue("name"));
					swimlaneAssignee.setProviderId(assigneeElement.attributeValue("provider-id"));
					swimlane.getSwimlaneAssignees().add(swimlaneAssignee);
				}
			}
			process.getSwimlanes().add(swimlane);
		}
	}

	private void deserializerNode(Process process, Element rootElement) throws Exception {
		for (Object obj : rootElement.elements()) {
			Element element = (Element) obj;
			for (ShapeDeserializer shapeDeserializer : shapeDeserializerColl) {
				if (shapeDeserializer.support(element.getName())) {
					Shape childShape = shapeDeserializer.execute(element);
					if (childShape instanceof Node) {
						process.getNodes().add((Node) childShape);
					}
				}
			}
		}
	}

	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.shapeDeserializerColl = applicationContext.getBeansOfType(ShapeDeserializer.class).values();
	}

}
